import dvaModelExtend from 'dva-model-extend'
import { cloneDeep, defaultsDeep } from 'lodash-es'

import { routesMap } from '@/router/index'
import { getUrlParams } from '@/router/navigate'

import { COMMON_REDUCER } from './dvaEffectsEnhancer'

import { argumentHelp } from './index'

/**
 * @typedef {object} modelExtendProps
 * @property {string} [matchedPageKeys] 匹配的其他pageKey数组
 * @property {function(function, {})} [matchedActions] 匹配到路由后执行该函数
 * @property {string} [namespace] model 的命名空间，也是pageKey对应的值
 * 同时也是他在全局 state 上的属性，
 * 只能用字符串，不支持通过 . 的方式创建多层命名空间。
 *
 * @property {object} [state] 初始值，优先级低于传给 dva() 的 opts.initialState
 * @property {object} [reducers] 以 key/value 格式定义 reducer。
 * 用于处理同步操作，唯一可以修改 state 的地方。
 * 由 action 触发。
 * 格式为 (state, action) => newState 或 [(state, action) => newState, enhancer]。
 *
 * @property {object} [effects] 以 key/value 格式定义 effect。
 * 用于处理异步操作和业务逻辑，不直接修改 state。
 * 由 action 触发，可以触发 action，可以和服务器交互，可以获取全局 state 的数据等等。
 * 格式为 *(action, effects) => void 或 [*(action, effects) => void, { type }]。
 *
 * @property {object} [subscriptions] 以 key/value 格式定义 subscription。
 * subscription 是订阅，用于订阅一个数据源，然后根据需要 dispatch 相应的 action。
 * 在 app.start() 时被执行，
 * 数据源可以是当前的时间、服务器的 websocket 连接、keyboard 输入、geolocation 变化、history 路由变化等等。
 * 格式为 ({ dispatch, history }, done) => unlistenFunction。
 * 注意：如果要使用 app.unmodel()，subscription 必须返回 unlisten 方法，用于取消数据订阅。
 */

/**
 * @param {modelExtendProps} model
 * @returns
 */
const modelExtend = model => {
  const initialState = cloneDeep(model.state)
  const { namespace, matchedPageKeys, matchedActions } = model

  const matchOne = pathname => {
    return (matchedPageKeys || []).some(namespace => {
      return routesMap[namespace].path === pathname
    })
  }

  const matchUrl = pathname => {
    return pathname === routesMap[namespace]?.path || matchOne(pathname)
  }

  // 代管url匹配
  const subscriptions = {
    setup({ dispatch, history }) {
      const toAction = (...props) => {
        const { object: payload, string: type = COMMON_REDUCER, boolean: partObj = false } = argumentHelp(props)
        return dispatch({
          type,
          payload,
          partObj,
        })
      }

      return history.listen(({ pathname, search }) => {
        if (!matchUrl(pathname)) return
        const { action } = getUrlParams(search)
        // 调用navigateBack会加back，认为是返回该页面，则不重置state
        if (action !== 'back' && history.action !== 'POP') {
          toAction('reset')
        }
        const params = {}
        if (search) {
          search
            .substring(1)
            .split('&')
            .forEach(keyValue => {
              const [key, value] = keyValue.split('=')
              params[key] = JSON.parse(decodeURIComponent(value))
            })
        }
        matchedActions && matchedActions(toAction, params)
      })
    },
  }

  return dvaModelExtend(
    {
      subscriptions,
      effects: {
        *getState(_, { select }) {
          return yield select(state => state[namespace])
        },
      },
      reducers: {
        [COMMON_REDUCER](state, { payload = {}, partObj = false }) {
          if (partObj) {
            return defaultsDeep(payload, state)
          }
          return { ...state, ...payload }
        },
        reset() {
          return { ...initialState }
        },
      },
    },
    model,
  )
}
export default modelExtend
